package com.yiduo.outpost.core.redis.config;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.yiduo.outpost.core.redis.props.RedisConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@EnableCaching
@Configuration
@ConditionalOnProperty(value = RedisConstant.PREFIX + ".enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnClass({Cache.class, RedisTemplate.class})
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter(value = {RedisAutoConfiguration.class, org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration.class})
public class CacheAutoConfiguration {
    @Autowired
    private CacheProperties cacheProperties;
    /**
     * 配置Redis缓存
     * @return Redis缓存配置对象
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration() {
        // 获取Redis配置信息
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        // 创建JSON序列化器
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(RedisAutoConfiguration.buildObjectMapper(),Object.class);
        // 创建Redis缓存配置对象
        RedisCacheConfiguration configuration =
                RedisCacheConfiguration.defaultCacheConfig()
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer));
        // 设置缓存键的序列化器
        configuration = configuration.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        // 设置缓存的过期时间
        if (redisProperties.getTimeToLive() != null) {
            configuration = configuration.entryTtl(redisProperties.getTimeToLive());
        }
        // 设置缓存的前缀
        if (redisProperties.getKeyPrefix() != null) {
            configuration = configuration.prefixCacheNameWith(redisProperties.getKeyPrefix());
        }
        // 设置是否缓存空值
        if (!redisProperties.isCacheNullValues()) {
            configuration = configuration.disableCachingNullValues();
        }
        // 设置是否使用键前缀
        if (!redisProperties.isUseKeyPrefix()) {
            configuration = configuration.disableKeyPrefix();
        }
        return configuration;
    }

    /**
     * 创建一个Redis缓存管理器构建器自定义化对象，用于自定义Redis缓存管理器的构建
     *
     * @param redisTemplate Redis模板对象
     * @return Redis缓存管理器构建器自定义化对象
     */
    @Bean
    public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer(
            RedisTemplate<String, Object> redisTemplate) {
        return builder -> builder.cacheWriter(RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory()));
    }

    @Bean
    public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory,
                                               RedisCacheConfiguration redisCacheConfiguration) {
        RedisCacheManager rcm = RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(redisCacheConfiguration)
                .transactionAware()
                .build();
        return rcm;
    }

    /**
     * 创建一个Caffeine缓存管理器对象
     *
     * @return
     */
    @Bean
    public CacheManager caffeineCacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(Caffeine.from(cacheProperties.getCaffeine().getSpec()));
        return cacheManager;
    }

    @Bean
    @Primary
    public CacheManager cacheManager(@Qualifier("caffeineCacheManager") CaffeineCacheManager caffeineCacheManager,
                                     @Qualifier("redisCacheManager") RedisCacheManager redisCacheManager) {
        return new MultiLevelCacheManager(caffeineCacheManager, redisCacheManager);
    }

}